package concurrecy.lock;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

class LockChecker implements Runnable {

    private Lock lock;

    public LockChecker(Lock lock) {
        this.lock = lock;
    }

    @Override
    public String toString() {
        return Thread.currentThread().getName();
    }

    @Override
    public void run() {
        System.out.println(this + " 开始抢锁");
        lock.lock();
        System.out.println(this + " 抢到锁");
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            System.out.println(this + " sleep interrupted");
        } finally {
            lock.unlock();
            System.out.println(this + " 释放锁");
        }
    }
}

class MyLock implements Lock {

    // 当前锁的状态，0 未加锁。  大于0 加锁
    protected AtomicInteger state = new AtomicInteger(0);
    // 当前锁的拥有者
    protected Thread owner;
    // 等待队列
    protected BlockingQueue<Thread> waitQueue = new LinkedBlockingQueue<>();

    @Override
    public boolean tryLock() {
        if(state.get() == 0 && state.compareAndSet(0, 1)) {
            // 抢到锁
            owner = Thread.currentThread();
            return true;
        }
        return false;
    }

    @Override
    public void lock() {
        if(!tryLock()) {
            // 没有抢到锁，加入等待队列
            waitQueue.add(Thread.currentThread());
            for(;;) {
                if(tryLock()) {
                    // 如果是队列头部元素抢到锁，移除头部元素
                    if(owner == waitQueue.peek())
                        waitQueue.poll();
                    else
                        // 是一个插队的线程抢到锁
                        waitQueue.remove(Thread.currentThread());
                    return;
                }
                LockSupport.park(); // 阻塞当前线程
            }
        }
    }

    @Override
    public void unlock() {
        if(Thread.currentThread() != owner)
            throw new RuntimeException("不是你的锁");
        if(state.decrementAndGet() == 0) {
            // 解锁成功
            owner = null;
            // 通知等待线程
            Thread t = waitQueue.peek();
            if(t != null)
                LockSupport.unpark(t);
        }
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    @Override
    public Condition newCondition() {
        return null;
    }
}

/**
 * 自己实现一把锁
 */
public class MyLockDemo {

    /**
     * 测试多线程获取锁
     * @param lock
     */
    static void test2(Lock lock) {
        ExecutorService service = Executors.newCachedThreadPool();
        service.execute(new LockChecker(lock));
        service.execute(new LockChecker(lock));
        service.execute(new LockChecker(lock));
        service.execute(new LockChecker(lock));
        service.shutdown();
    }

    /**
     * 测试多线程竞争
     */
    static void test3(Lock lock, int n) throws InterruptedException {
        class Data {
            int x = 0;
        }
        final Data data = new Data();
        ExecutorService executorService = Executors.newCachedThreadPool();
        CountDownLatch latch = new CountDownLatch(n);
        long point = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            executorService.execute(() -> {
                for (int j = 0; j < 100_000; j++) {
                    lock.lock();
                    try {
                        data.x ++;
                    } finally {
                        lock.unlock();
                    }
                }
                latch.countDown();
            });
        }
        latch.await();
        System.out.println("use " + (System.currentTimeMillis() - point));
        executorService.shutdown();
        System.out.println(data.x);
    }

    public static void main(String[] args) throws InterruptedException {
//        Lock lock = new MyLock();
        Lock lock = new ReentrantLock();
//        test2(lock);
        test3(lock, 8);
    }
}
